這篇文章,想跟大家分享用滑鼠互動的動畫效果,這是一個滑鼠移入後,會將文字打散,滑鼠移出後會還原文字的動畫效果,以下是範例:
首先在 html 寫好文字內容
<div id="text-container">
你好,我是 MUKI,很高興認識你,Nice to meet you.
</div>
先取得 HTML 中的文字元素,接下來,將 textContainer
的 innerHTML
設置為空字串,清除原有的內容。這一步是為了之後將每個字元包在 span
標籤中,為了獨立控制每個字元的動畫效果。
const textContainer = document.getElementById('text-container');
const textContent = textContainer.textContent;
textContainer.innerHTML = '';
span
標籤中textContent.split('').forEach(char => {
if (char === ' ') {
textContainer.appendChild(document.createTextNode(' '));
} else {
const span = document.createElement('span');
span.className = 'char';
span.textContent = char;
textContainer.appendChild(span);
}
});
將 textContent
分解為獨立字元後,先檢查這些字元是否為空白字元,如果是空白字元,就加入一個空白的 TextNode
到 textContainer
,因為我的原文有空格,這樣做是為了保留原文中的空白字元。
對於非空白字元,就將每個字元單獨包在 span
標籤裡,為了可以分開控制每個字元的動畫效果。
使用 mouseenter
事件監聽器,透過 forEach
迴圈,對每個字元進行處理。
因為我希望每次滑鼠移入後,打散的位置都是隨機的,所以用 Math.random()
產生隨機的 X 軸和 Y 軸,以及隨機的旋轉角度。最後用到 animate()
方法,設定開始跟結束的位移與旋轉角度,特別注意的是動畫結束後,這些元素會保持在動畫的終止狀態 (fill: 'forwards'
),不要讓他回到起點唷
textContainer.addEventListener('mouseenter', () => {
chars.forEach(char => {
const randomX = (Math.random() - 0.5) * 300;
const randomY = (Math.random() - 0.5) * 300;
const randomRotation = Math.random() * 360;
char.animate([
{ transform: 'translate(0, 0) rotate(0deg)' },
{ transform: `translate(${randomX}px, ${randomY}px) rotate(${randomRotation}deg)` }
], {
duration: 1000,
fill: 'forwards'
});
});
});
使用 mouseleave
事件監聽器,將起始與終止的位置顛倒,就能讓文字恢復正常了。
通常會建議紀錄滑鼠移入後,文字的終止位置,再把他當成滑鼠移出時文字的起始位置,但我覺得都是亂數的效果也不錯,在這個範例就沒有那麼講究了。
textContainer.addEventListener('mouseleave', () => {
chars.forEach(char => {
const randomX = (Math.random() - 0.5) * 300;
const randomY = (Math.random() - 0.5) * 300;
const randomRotation = Math.random() * 360;
char.animate([
{ transform: `translate(${randomX}px, ${randomY}px) rotate(${randomRotation}deg)` },
{ transform: 'translate(0, 0) rotate(0deg)' }
], {
duration: 1000,
fill: 'forwards'
});
});
});
大家有興趣的話,可以再將這個程式碼修改成你喜歡的樣子
const textContainer = document.getElementById('text-container');
const textContent = textContainer.textContent;
textContainer.innerHTML = '';
textContent.split('').forEach(char => {
if (char === ' ') {
textContainer.appendChild(document.createTextNode(' '));
} else {
const span = document.createElement('span');
span.className = 'char';
span.textContent = char;
textContainer.appendChild(span);
}
});
const chars = document.querySelectorAll('.char');
textContainer.addEventListener('mouseenter', () => {
chars.forEach(char => {
const randomX = (Math.random() - 0.5) * 300;
const randomY = (Math.random() - 0.5) * 300;
const randomRotation = Math.random() * 360;
char.animate([
{ transform: 'translate(0, 0) rotate(0deg)' },
{ transform: `translate(${randomX}px, ${randomY}px) rotate(${randomRotation}deg)` }
], {
duration: 1000,
fill: 'forwards'
});
});
});
textContainer.addEventListener('mouseleave', () => {
chars.forEach(char => {
const randomX = (Math.random() - 0.5) * 300;
const randomY = (Math.random() - 0.5) * 300;
const randomRotation = Math.random() * 360;
char.animate([
{ transform: `translate(${randomX}px, ${randomY}px) rotate(${randomRotation}deg)` },
{ transform: 'translate(0, 0) rotate(0deg)' }
], {
duration: 1000,
fill: 'forwards'
});
});
});
範例程式碼:https://mukiwu.github.io/web-api-demo/animation.html
以上有任何問題,都歡迎留言討論。